package com.kate.mall.common.utils;

/**
 * @author 新梦想.陈超
 * @version 2021.2
 * @Description: {TODO}
 * @date 2023/3/7 上午10:46
 */

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

/**
 * map{
 * "id":1
 * "name":"abc"
 * "price": 12.9
 * }
 * map{
 * "id":1
 * "name":"abc"
 * "price": 12.9
 * }
 * VO{
 * private int id;
 * private String name;
 * }
 */
@Component
@Slf4j
public class ListMap2VoUtil{
    @Resource
    private  RedisUtils<List> redisUtils;

    @Resource
    ObjectMapper objectMapper;

    //知识点1：T 泛型通配符，在编译时才确定T的类型
    //知识点2：? extends T 泛型上限
    //知识点3：反射
    public   <T> List<T> map2vo(List<Map> source, Class<? extends T> clazz) {
        //TODO 1.list结果集合
        List<T> result =new ArrayList<>() ;
        //如果每次source是相同的，那我就将第1次反射的结果缓存起来，当第2次传入相同的source的时候，
        // 就直接从缓存中获取，而不要进行反射，从而提高我们的这个工具的性能，尤其是在高并发场景下
        /*生成源数据的摘要，并以源数据的摘要作为缓存key,value为解析过后的数据*/
        //TODO 2.根据source的md5值，查询缓存
        String md5Key = null;
        try {
            md5Key = MD5Utils.md5Hex(objectToByteArray(source));
            result =redisUtils.get(md5Key,List.class);
            result = objectMapper.convertValue(result, new TypeReference<List<T>>(){});
            if (result != null) {
                log.info("map2vo 从redis缓存中获取List<Vo>......");
                return result;
            }
            result= new ArrayList<>();
            log.info("map2vo 通过反射获取List<Vo>......");
            //TODO 3.获取VO类的属性名
            //a.获取vo类的属性对象
            //Field[] targetFields = clazz.getDeclaredFields(); 无法获取父类的属性   class.getFields() 获取
            Field[] targetFields = ReflectUtil.getFields(clazz);
            //破解封装
            Field.setAccessible(targetFields, true);
            for (Map filedMap : source) {
                //TODO 4.创建Vo对象
                T targetObj = clazz.newInstance();
                for (Field targetField : targetFields) {
                    //b.获取属性名
                    String fieldName = targetField.getName();
                    //TODO 5.从map中根据属性名为key，来获取值
                    Object fieldVale = filedMap.get(fieldName);
                    //TODO 6.给vo对象的fieldName属性赋值为fieldVale
                    //将原数据类型的double值转为目标数据类的属性类型BigDecimal
                    if (targetField.getType().equals(BigDecimal.class)) {
                        targetField.set(targetObj, new BigDecimal(String.valueOf((double) fieldVale)));
                        continue;
                    }
                    //TODO 8.2 如果isNew、isHot属性类型integer转boolean
                    //isNew、isHot属性类型integer转boolean
                    if (targetField.getName().equals("isNew")) {
                        //TODO 8.2.1.将转换的值设置到目标对象上
                        targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
                        continue;
                    }

                    //isNew、isHot属性类型integer转boolean
                    if (targetField.getName().equals("isHot")) {
                        targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
                        continue;
                    }

                    targetField.set(targetObj, fieldVale);

                }
                //TODO 7.将对象放入集合
                result.add(targetObj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //TODO 8.将反射解析的结果存到redis
        redisUtils.set(md5Key,result);
        return result;
    }

    /**
     * 对象转字节数组
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static byte[] objectToByteArray(Object obj) throws Exception {
        byte[] bytes = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        bytes = baos.toByteArray();
        baos.close();
        return bytes;
    }

    public static void main(String[] args) throws Exception {
        //1.list<map>
        Map m1 = new LinkedHashMap();
        m1.put("id", 1);
        m1.put("name", "kate");
        m1.put("floorPrice", 29.9);

        Map m2 = new LinkedHashMap();
        m2.put("id", 2);
        m2.put("name", "kate2");
        m2.put("floorPrice", 59.9);


        Map m3 = new LinkedHashMap();
        m3.put("id", 3);
        m3.put("name", "kate2");
        m3.put("floorPrice", 59.9);

        List<Map> source1 = Arrays.asList(m2);
        List<Map> source2 = Arrays.asList(m3);

        String s1 = MD5Utils.md5Hex(objectToByteArray(source1));
        String s2 = MD5Utils.md5Hex(objectToByteArray(source2));
        System.out.println(s1.equals(s2));

        //2.vo---BrandVo

        //3.调用list<map>转list<vo>
//        List<BrandVO> brandVOS = ListMap2VoUtil.map2vo(source, BrandVO.class);
//        System.out.println(brandVOS);


        Integer a = 127;
        Integer b = 127;
        System.out.println(a == b);//true

        a = 128;
        b = 128;
        System.out.println(a == b);//false


    }
}


class BrandVO {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "BrandVO{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}



